package main

import (
    "bufio"
	"fmt"
	"io"
	"os/exec"
	"sync"
	"strconv"
	"strings"
	"time"
)

func main() {
    headers()
    var menuNum int
    fmt.Printf("\x1b[%dm 请输入执行序号：\x1b[0m", 33)
    fmt.Scanln(&menuNum)

	switch menuNum {
        case 1:
            install_docker()
        case 2:
            node_add()
        case 3:
            node_restart()
        case 4:
            node_getAll()
        case 5:
            node_info()
        case 6:
            node_logs()
        case 7:
            node_docker_in()
        case 8:
            nodeErrUpdate()
        case 22:
            node_add_n()
        case 33:
            node_backup()
        case 97:
            rebootNode()
        case 98:
            node_remove()
        case 99:
            goUpdate()
        case 100:
            pullDocker()
            main()
        case 1024:
            sendfrom()
      default:
   }

}

func headers(){
    fmt.Printf("\x1b[%dm ------------------------------------------------------ \x1b[0m\n", 33)
    fmt.Printf("\x1b[%dm┊                二哥的EBZZ Docker                     ┊\x1b[0m\n", 33)
    fmt.Printf("\x1b[%dm┊  捐款吗？兄弟！                最后更新：2021-09-07  ┊\x1b[0m\n", 33)
    fmt.Printf("\x1b[%dm┊  ETH: 0x0f8a4591CF59c938165D7B994528fe48BDBDb0eA     ┊\x1b[0m\n", 33)
    fmt.Printf("┊ ---------------------------------------------------- ┊\n")
    fmt.Printf("┊ %-5s %-17s %3s %-16s ┊\n", " 2", "添加节点", " 1", "安装docker环境")
    fmt.Printf("┊ %-5s %-17s %3s %-14s ┊\n", " 3", "重启节点" ,"97", "重启所有容器")
    fmt.Printf("┊ %-5s %-13s %3s %-14s ┊\n", " 4", "查看所有节点信息" ,"99", "更新操作面板")
    fmt.Printf("┊ %-5s %-13s %3s %-15s ┊\n", " 5", "查看节点详细数据" ,"100", "更新挖矿端")
    fmt.Printf("┊ %-5s %-15s %3s %-18s ┊\n", " 6", "查看指定日志", "0", "退出")
    fmt.Printf("┊ %-5s %-17s %3s %-18s ┊\n", " 7", "进入指定容器" ,"", "")
    fmt.Printf("┊ %-5s %-17s %3s %-16s ┊\n", " 8", "更新所有异常节点" ,"", "")
    fmt.Printf("┊ %-5s %-17s %3s %-18s ┊\n", " 22", "批量添加节点" ,"", "")
    fmt.Printf("┊ %-5s %-17s %3s %-16s ┊\n", " 33", "备份所有节点钱包" ,"", "")
    fmt.Printf("┊ %-5s %-17s %3s %-14s ┊\n", " 98", "移除节点（保留数据）" ,"", "")
    fmt.Printf("┊%-6s %-17s %3s %-15s ┊\n", " 1024", "汇集产出到指定钱包" ,"", "")
    fmt.Println(" ------------------------------------------------------ ")
}

//docker环境安装
func install_docker(){
    Commands(`apt install curl -y
    sudo curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun`)
    pullDocker()
}

//添加一个容器节点
func node_add(){
    var nums int
    var nodeIP string

    fmt.Printf("输入需要添加单节点序号(数字)：")
    fmt.Scanln(&nums)

    fmt.Printf("请输入引导IP(IP:端口 可不填)：")
    fmt.Scanln(&nodeIP)
    _createNode(nums , nodeIP)
    _luca()
}

//重启一个容器
func node_restart(){
    var nums string
    fmt.Printf("输入需要重启的节点序号：")

    fmt.Scanln(&nums)

    Commands(fmt.Sprintf(`
    docker exec ebzz-node-%s bash -c "cd /ebzz && ./cli bzz stop"
    docker restart ebzz-node-%s
    `, nums, nums))

    fmt.Println("-----------------------------------------")

    _luca()
}


//查看所有容器
func node_getAll(){

    countSplit := _getALLnode()
    fmt.Println("---------------------------------------------------------------------------")
    fmt.Printf("%4s   %-30s   %-8s  %-15s\n", "节点", "钱包地址", "余额", "区块高度")
    fmt.Println("---------------------------------------------------------------------------")
    var total float64
    for i := range countSplit {
        nodeN := countSplit[i]
        bzzBalance := _getBalance(nodeN)
        block := _getBlock(nodeN)
        address := _getAddress(nodeN)
        v , _ := strconv.ParseFloat(bzzBalance, 64)
        total += v
        fmt.Printf("%4d     %-34s   %-10s  %-15s\n", i+1, address ,bzzBalance, block)
    }

    tos := fmt.Sprintf("%.4f", total)

    //当前时间戳
    t1 := time.Now().Format("2006-01-02 15:04:05")
    fmt.Println("---------------------------------------------------------------------------")
    fmt.Printf("%6s  %-20s %17s   %-20s\n", "统计时间", t1,"总计", tos+" EBZZ")
    fmt.Println("---------------------------------------------------------------------------")

    _luca()
}


//查看指定容器日志
func node_logs(){
    var nums string
    fmt.Printf("输入需要查看日志的节点序号：")
    fmt.Scanln(&nums)
    Commands("docker logs -f --tail=100 ebzz-node-"+nums);
}

//批量添加 n 个节点
func node_add_n(){
    var start , end int
    var nodeIP string
    fmt.Printf("批量添加起始序号：")
    fmt.Scanln(&start)

    fmt.Printf("添加多少个：")
    fmt.Scanln(&end)

    fmt.Printf("请输入引导IP(IP:端口 可不填)：")
    fmt.Scanln(&nodeIP)

    for i := start; i <= end; i++ {
        _createNode(i, nodeIP)
    }
    node_getAll()
}


//备份所有节点钱包
func node_backup(){
    var backMark string
    fmt.Printf("请输入备份前缀(如本机IP)：")
    fmt.Scanln(&backMark)
    countSplit := _getALLnode()
	for i := range countSplit {
        nodeN := "node-"+countSplit[i]
        fname := "ebzzWallet_backup_"+backMark+"_"+nodeN+".tar.gz"
        ExecCommand(`cd /ebzzData/ && tar czvf `+fname+` `+nodeN+`/entities.dat params.dat  `+nodeN+`/peers.dat  `+nodeN+`/seed.dat  `+nodeN+`/swarmbzz.conf `+nodeN+`/swarmbzz.pid `+nodeN+`/wallet.dat `+nodeN+`/wallet/ 2>/dev/null`)
		fmt.Println("--------------- ("+nodeN+")备份完成：/ebzzData/"+fname+" ---------------\n\n")
	}
	_luca()
}

//移除一个容器(不会删除节点数据)
func node_remove(){

    fmt.Println("--- 该操作不会删除节点数据，可放心移除 ---")
    var start , end int
    fmt.Printf("输入需要移除的节点【起始】序号：")
    fmt.Scanln(&start)
    fmt.Printf("输入需要移除的节点【结束】序号：")
    fmt.Scanln(&end)

    fmt.Println("-----------------------------------------")
    for i := start; i <= end; i++ {
        n := strconv.Itoa(i)
        Command(`docker exec ebzz-node-`+n+` bash -c "cd /ebzz && ./cli bzz stop"`)
        Command(`docker stop ebzz-node-`+n)
        Command(`docker rm -f ebzz-node-`+n)
    }
    fmt.Println("-----------------------------------------")
    _luca()
}

//进入指定docker容器
func node_docker_in(){
  var nums string
  fmt.Printf("输入需要进入的节点序号：")
  fmt.Scanln(&nums)
  fmt.Println("-----------------------------------------")
  fmt.Println(`请手动执行以下命令进入容器：`)
  fmt.Println(`sudo docker exec -itd ebzz-node-`+nums+` /bin/bash`)
  fmt.Println("-----------------------------------------")
  _luca()
}


//汇集
func sendfrom(){
    var toAddress string
    fmt.Printf("请输入汇集目标钱包地址：")
    fmt.Scanln(&toAddress)

    if toAddress == "" {
        fmt.Println("汇集目标钱包地址不存在！退出...")
        return
    }
    var yes string
    fmt.Printf("所有节点产出汇集到【$toAddress】，确认请输入(y) ：")
    fmt.Scanln(&yes)

    if len(yes) == 0{
        fmt.Println("-------------- 本机所有节点产出汇集以下地址 --------------")
        fmt.Println("--------  "+toAddress+"  --------")
    }else{
        return
    }

    countSplit := _getALLnode()
  	for i := range countSplit {
        nodeN := countSplit[i]
        bzzBalance := _getBalance(nodeN)
        address := _getAddress(nodeN)
        fmt.Println("-----------------------------------------")
        Commands(`docker exec ebzz-node-`+nodeN+` bash -c "cd /ebzz && ./cli bzz sendfrom `+address+` `+toAddress+` `+bzzBalance+`"`)
  	    fmt.Println(`--------------- (node-`+nodeN+`)汇集完成 `+bzzBalance+` To: `+toAddress+` ---------------`)
  	}
    _luca()
}


//查看节点详细数据
func node_info(){
    var nums string
    fmt.Printf("请输入需要查看的节点序号：")
    fmt.Scanln(&nums)

    fmt.Println(`--------------------- 容器名称：ebzz-node-`+nums+` -------------------`)
    //查询当前钱包地址
    address := _getAddress(nums)
    fmt.Println(`钱包地址：`+address)

    //查询余额
    bzzBalance := _getBalance(nums)
    fmt.Println(`当前余额：`+bzzBalance)
    //区块高度
    block := _getBlock(nums)
    fmt.Println(`区块高度：`+block)

    //当前节点监听数据
    Commands(`docker exec $nums bash -c "cd /ebzz && ./cli bzz listaddresstransactions $address" | grep -o -E '("amount" : [0-9\.]+)|("txid" : "[0-9a-zA-Z]+")|("timereceived" : [0-9]+)' | sed -e 's/\s//g' | sed 's/"amount":/=========\n交易数量：/g' | sed 's/"txid":/交易txid：/g' | sed 's/"timereceived":/确认时间：/g' | sed 's/"//g'`)

    fmt.Println("-----------------------------------------")
    _luca()
}


//更新docker
func pullDocker(){
    Commands(`docker pull ebzz/ubuntu:latest`)
}

//重启所有节点
func rebootNode(){
    countSplit := _getALLnode()
    for i := range countSplit {
        nodeN := countSplit[i]
        Command(`docker exec ebzz-node-`+nodeN+` bash -c "cd /ebzz && ./cli bzz stop"`)
        Commands(`docker restart ebzz-node-`+nodeN+``)
        fmt.Println(`--------------- node-`+nodeN+` 重启完成 ---------------`)
    }
    _luca()
}

//更新节点异常状态
func nodeErrUpdate(){
    countSplit := _getALLnode()
    for i := range countSplit {
        nodeN := countSplit[i]
        address := _getAddress(nodeN)
        fmt.Println(ExecCommand(`curl -s http://106.52.187.241:6668/action?token=XtjyN3qTgfAeKUcqzwCNkDbUsapL&addresses=`+address))
        fmt.Println(`--------------- node-`+nodeN+` 更新完成 ---------------`)
    }
    _luca()
}

//更新控制面板
func goUpdate(){
    fmt.Println(`--------------- GitHub网络通讯差，可能要多更新几次 ---------------`)
    fmt.Println(`--------------- 看最后更新日期是不是最新的 ---------------`)
    fmt.Println(`--------------- 长时间不响应，ctrl+c后重新运行此脚本 ---------------`)
    fmt.Println(`--------------- 执行命令： ---------------`)
    fmt.Println(`wget https://github.com/erge-bion/ebzz/raw/main/ebzzDocker -O /root/ebzzDocker`)
    fmt.Println(`chmod +x /root/ebzzDocker`)
    fmt.Println(`--------------- 更新中... ---------------`)
    Commands(`wget https://github.com/erge-bion/ebzz/raw/main/ebzzDocker -O /root/ebzzDocker`)
    Commands(`chmod +x /root/ebzzDocker`)

    fmt.Println(`--------------- 更新完成，请手动敲入命令：./ebzzDocker`)
}

//命令执行后任意键退出并叫起控制面板
func _luca(){
    s :=""
    fmt.Printf("███ -> Enter键退出")
    fmt.Scanln(&s)
    main()
}

//获取当前所有docker节点
func _getALLnode() []string {
    dockerList := ExecCommand(`docker ps -qa  --filter "name=ebzz-node-*" --format "table {{.Names}}"|grep -v NAMES | grep -o -E '[0-9]+'`)
    countSplit := strings.Split(strings.Trim(dockerList, "\n"), "\n")
    return countSplit;
}

//获取指定节点 钱包地址
func _getAddress (node string) string{
    str := ExecCommand(`docker exec ebzz-node-`+node+` bash -c "grep -a -o -E '[0-9a-zA-Z]+{34}' /root/.swarmbzz/bzz/wallet.dat | head -n1" | sed -e 's/\s//g'`)
    str = strings.Trim(str, " ")
    str = strings.Trim(str, "\n")
    return str
}

//获取指定节点 账户余额
func _getBalance (node string) string{
    str := ExecCommand(`docker exec ebzz-node-`+node+` bash -c "cd /ebzz && ./cli bzz getinfo" | grep -o -E '"balance_bzz" : [0-9\.]+'  | awk '{print $3}'`)
    str = strings.Trim(str, " ")
    str = strings.Trim(str, "\n")
    return str
}

//获取指定节点 区块高度
func _getBlock (node string) string{
    str := ExecCommand(`docker exec ebzz-node-`+node+` bash -c "cd /ebzz && ./cli bzz getinfo" | grep -o -E '"blocks" : [0-9]+'  | awk '{print $3}'`)
    str = strings.Trim(str, " ")
    str = strings.Trim(str, "\n")
    return str
}

//公共函数 创建一个节点
func _createNode(nums int, ips string){
  p := (nums * 10 + 1000)
  port := strconv.Itoa(p);
  port2 := strconv.Itoa(p + 1)
  nodenums := strconv.Itoa(nums)

  Command("docker run --restart=always --name ebzz-node-"+nodenums+" -d -p "+port+":6288  -p "+port2+":6289 -v /ebzzData/node-"+nodenums+":/root/.swarmbzz/bzz ebzz/ubuntu /EBZZ-start.sh "+ips)

}


//执行shell 默认
func Command(cmd string) error {
    c := exec.Command("/bin/bash", "-c", cmd)
    output, err := c.CombinedOutput()

    fmt.Println(string(output))
    return err
}

//执行shell 实时返回结果
func Commands(cmd string) error {
	c := exec.Command("/bin/bash", "-c", cmd)  // mac or linux
	stdout, err := c.StdoutPipe()
	if err != nil {
		return err
	}
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		reader := bufio.NewReader(stdout)
		for {
			readString, err := reader.ReadString('\n')
			if err != nil || err == io.EOF {
				return
			}
			fmt.Print(readString)
		}
	}()
	err = c.Start()
	wg.Wait()
	return err
}

func ExecCommand(cmd string) string {
	result, err := exec.Command("/bin/bash", "-c", cmd).Output()
	if err != nil {
        fmt.Println("命令执行失败：" + cmd)
		fmt.Println(err.Error())
	}
	return string(result)
}
